<?php

/**
 * JPGraph v4.0.3
 */

namespace lemon\JpGraph\Util;

/**
 * File:		JPGRAPH_REGSTAT.PHP
 * // Description: Regression and statistical analysis helper classes
 * // Created:	 2002-12-01
 * // Ver:		 $Id: jpgraph_regstat.php 1131 2009-03-11 20:08:24Z ljp $
 * //
 * // Copyright (c) Asial Corporation. All rights reserved.
 */

/**
 * @class Spline
 * // Create a new data array from an existing data array but with more points.
 * // The new points are interpolated using a cubic spline algorithm
 */
class Spline
{
	// 3:rd degree polynom approximation

	private $xdata;
	private $ydata; // Data vectors
	private $y2; // 2:nd derivate of ydata
	private $n = 0;

	public function __construct($xdata, $ydata)
	{
		$this->y2	= [];
		$this->xdata = $xdata;
		$this->ydata = $ydata;

		$n	   = safe_count($ydata);
		$this->n = $n;
		if ($this->n !== safe_count($xdata)) {
			JpGraphError::RaiseL(19001);
			//('Spline: Number of X and Y coordinates must be the same');
		}

		// Natural spline 2:derivate == 0 at endpoints
		$this->y2[0]	  = 0.0;
		$this->y2[$n - 1] = 0.0;
		$delta[0]		 = 0.0;

		// Calculate 2:nd derivate
		for ($i = 1; $i < $n - 1; ++$i) {
			$d = ($xdata[$i + 1] - $xdata[$i - 1]);
			if ($d == 0) {
				JpGraphError::RaiseL(19002);
				//('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
			}
			$s			= ($xdata[$i] - $xdata[$i - 1]) / $d;
			$p			= $s * $this->y2[$i - 1] + 2.0;
			$this->y2[$i] = ($s - 1.0) / $p;
			$delta[$i]	= ($ydata[$i + 1] - $ydata[$i]) / ($xdata[$i + 1] - $xdata[$i]) -
				($ydata[$i] - $ydata[$i - 1]) / ($xdata[$i] - $xdata[$i - 1]);
			$delta[$i] = (6.0 * $delta[$i] / ($xdata[$i + 1] - $xdata[$i - 1]) - $s * $delta[$i - 1]) / $p;
		}

		// Backward substitution
		for ($j = $n - 2; $j >= 0; --$j) {
			$this->y2[$j] = $this->y2[$j] * $this->y2[$j + 1] + $delta[$j];
		}
	}

	// Return the two new data vectors
	public function Get($num = 50)
	{
		$n	   = $this->n;
		$step	= ($this->xdata[$n - 1] - $this->xdata[0]) / ($num - 1);
		$xnew	= [];
		$ynew	= [];
		$xnew[0] = $this->xdata[0];
		$ynew[0] = $this->ydata[0];
		for ($j = 1; $j < $num; ++$j) {
			$xnew[$j] = $xnew[0] + $j * $step;
			$ynew[$j] = $this->Interpolate($xnew[$j]);
		}

		return [$xnew, $ynew];
	}

	// Return a single interpolated Y-value from an x value
	public function Interpolate($xpoint)
	{
		$max = $this->n - 1;
		$min = 0;

		// Binary search to find interval
		while ($max - $min > 1) {
			$k = ($max + $min) / 2;
			if ($this->xdata[$k] > $xpoint) {
				$max = $k;
			} else {
				$min = $k;
			}
		}

		// Each interval is interpolated by a 3:degree polynom function
		$h = $this->xdata[$max] - $this->xdata[$min];

		if ($h == 0) {
			JpGraphError::RaiseL(19002);
			//('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
		}

		$a = ($this->xdata[$max] - $xpoint) / $h;
		$b = ($xpoint - $this->xdata[$min]) / $h;

		return $a * $this->ydata[$min] + $b * $this->ydata[$max] +
			(($a * $a * $a - $a) * $this->y2[$min] + ($b * $b * $b - $b) * $this->y2[$max]) * ($h * $h) / 6.0;
	}
}

// EOF
